import os
import time
import json
import logging
import pickle
from datetime import datetime
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import undetected_chromedriver as uc
import urllib3
import warnings

# 修复 PoolWarning 导入问题
try:
    from urllib3.exceptions import PoolWarning
    warnings.filterwarnings("ignore", category=PoolWarning)
except ImportError:
    # 在较新版本的 urllib3 中，PoolWarning 可能不存在
    pass

# 减少连接池警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
urllib3.connectionpool.ConnectionPool.MAXSIZE = 50

from sqlalchemy.orm import Session
from models.comment_schema import RawComment

from data_collector.interfaces.comment_collector_interface import CommentCollector

# 配置日志
logger = logging.getLogger(__name__)

class DiscordCommentCollector(CommentCollector):
    def __init__(self, api_keys=None):
        """
        初始化 Discord 评论采集器
        :param api_keys: API 密钥列表（此处用于传递配置信息）
        """
        super().__init__(api_keys)
        
        # 配置信息
        self.DISCORD_EMAIL = os.getenv("DISCORD_EMAIL", "876245986@qq.com")
        self.DISCORD_PASSWORD = os.getenv("DISCORD_PASSWORD", "g038016870924G")
        self.CHANNEL_URL = os.getenv("DISCORD_CHANNEL_URL", "https://discord.com/channels/1124380978400481491/1130739053944373358")
        self.COOKIE_FILE = os.path.join("output", "discord", "discord_cookies.pkl")
        self.OUTPUT_DIR = os.path.join("output", "discord")
        os.makedirs(self.OUTPUT_DIR, exist_ok=True)
        
        # 设置代理
        os.environ.pop("HTTP_PROXY", None)
        os.environ.pop("HTTPS_PROXY", None)
        
        # 初始化浏览器驱动
        self.driver = None

    def _create_driver(self):
        """创建浏览器驱动"""
        options = uc.ChromeOptions()
        # 检查Chrome安装路径是否存在，如果不存在则使用默认路径
        chrome_path = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
        if os.path.exists(chrome_path):
            options.binary_location = chrome_path
            
        options.add_argument("--proxy-server=http://127.0.0.1:7890")  # Clash代理
        options.add_argument("--no-sandbox")
        options.add_argument("--disable-dev-shm-usage")
        options.add_argument("--disable-blink-features=AutomationControlled")
        options.add_argument("blink-settings=imagesEnabled=false")
        options.add_argument("--headless=new")  # 可选：无头模式

        try:
            logger.info("🚀 初始化 Chrome 驱动中...")
            driver = uc.Chrome(
                options=options,
                enable_cdp_events=True,
                use_subprocess=True
            )
            driver.set_page_load_timeout(60)
            return driver
        except Exception as e:
            logger.error(f"❌ 创建 Chrome 驱动失败: {e}")
            raise

    def _login_with_cookie(self, driver):
        """尝试使用保存的 Cookie 登录"""
        logger.info("🔄 尝试使用 Cookie 登录...")
        try:
            driver.get("https://discord.com/login")
            time.sleep(3)
            
            # 加载 Cookie
            if os.path.exists(self.COOKIE_FILE):
                cookies = pickle.load(open(self.COOKIE_FILE, "rb"))
                for cookie in cookies:
                    # 确保 cookie 有正确的域名
                    if 'domain' not in cookie:
                        cookie['domain'] = '.discord.com'
                    driver.add_cookie(cookie)
            
            # 访问频道页面验证登录状态
            driver.get(self.CHANNEL_URL)
            time.sleep(5)
            
            # 检查是否成功登录
            if "discord.com/channels" in driver.current_url:
                logger.info("✅ Cookie 登录成功！")
                return True
            else:
                logger.warning("⚠️ Cookie 登录失败，需要重新登录")
                return False
        except Exception as e:
            logger.warning(f"⚠️ Cookie 登录失败: {e}")
            return False

    def _manual_login(self, driver):
        """手动登录并保存 Cookie"""
        logger.info("🔐 正在打开 Discord 登录页面...")
        try:
            driver.get("https://discord.com/login")
            WebDriverWait(driver, 60).until(
                EC.presence_of_element_located((By.NAME, "email"))
            )
            driver.find_element(By.NAME, "email").send_keys(self.DISCORD_EMAIL)
            driver.find_element(By.NAME, "password").send_keys(self.DISCORD_PASSWORD)
            driver.find_element(By.XPATH, "//button[@type='submit']").click()
            WebDriverWait(driver, 60).until(
                EC.url_contains("discord.com/channels")
            )
            logger.info("✅ 登录成功！")
            
            # 保存 Cookie
            os.makedirs(os.path.dirname(self.COOKIE_FILE), exist_ok=True)
            pickle.dump(driver.get_cookies(), open(self.COOKIE_FILE, "wb"))
            logger.info("💾 Cookie 已保存，下次可自动登录")
            return True
        except Exception as e:
            logger.error(f"❌ 登录失败：{e}")
            ts = time.strftime("%Y%m%d_%H%M%S")
            driver.save_screenshot(f"discord_login_fail_{ts}.png")
            with open(f"discord_login_fail_{ts}.html", "w", encoding="utf-8") as f:
                f.write(driver.page_source)
            logger.info("📸 已保存失败页面截图和 HTML")
            raise

    def _login_to_discord(self, driver):
        """主登录函数，优先尝试 Cookie 登录，失败则手动登录"""
        # 检查是否存在保存的 Cookie
        if os.path.exists(self.COOKIE_FILE):
            if self._login_with_cookie(driver):
                return  # Cookie 登录成功，直接返回
        
        # Cookie 登录失败或不存在，进行手动登录
        self._manual_login(driver)

    def get_comments(self, video_info, max_comments_per_video=5000):
        """
        获取 Discord 频道中的消息
        :param video_info: 包含 id 和 video_id 的字典
        :param max_comments_per_video: 最大评论数
        :return: RawComment 列表
        """
        if not self.driver:
            self.driver = self._create_driver()
            
        try:
            self._login_to_discord(self.driver)
            return self._capture_all_messages(video_info, max_comments_per_video)
        except Exception as e:
            logger.error(f"❌ 获取 Discord 消息失败: {e}")
            return []

    def _capture_all_messages(self, video_info, max_comments_per_video):
        """捕获所有消息并转换为 RawComment 对象"""
        video_id = video_info.get("video_id", "")
        # 构造正确的 Discord 频道 URL
        if "/" in video_id and not video_id.startswith("http"):
            # 如果是 server_id/channel_id 格式
            channel_url = f"https://discord.com/channels/{video_id}"
        elif video_id.startswith("http"):
            # 如果已经是完整 URL
            channel_url = video_id
        else:
            # 默认格式
            channel_url = f"https://discord.com/channels/{video_id}"
        
        logger.info(f"📨 打开频道页面：{channel_url}")
        try:
            self.driver.get(channel_url)
            time.sleep(5)

            # 等待消息加载
            logger.info("⌛ 等待消息内容加载中...")
            WebDriverWait(self.driver, 30).until(
                lambda d: len(d.find_elements(By.CSS_SELECTOR, "[class*=messageContent]")) > 0
            )
            logger.info("✅ 消息区域已加载")

            try:
                messages_container = self.driver.find_element(By.CSS_SELECTOR, 'div[aria-label="Messages"]')
            except NoSuchElementException:
                logger.warning("⚠️ 未找到 aria-label=Messages 容器，使用原选择器")
                messages_container = self.driver.find_element(By.CSS_SELECTOR, "[class*=messagesWrapper], [class*=scrollerInner]")

            collected_messages = {}
            SCROLL_WAIT_TIME = 7  # 适度增加等待时间
            stagnant_rounds = 0
            max_stagnant_rounds = 3  # 大幅减少停滞轮次（从10减到3）
            
            # 获取容器高度
            container_height = self.driver.execute_script("return arguments[0].scrollHeight;", messages_container)
            logger.info(f"📏 消息容器高度: {container_height}px")
            
            # 优化滚动策略
            scroll_step = -int(container_height * 0.8)  # 每次滚动容器高度的80%

            last_message_count = 0
            scroll_attempts = 0
            max_scroll_attempts = 20  # 最大滚动尝试次数

            # 初始加载消息
            current_messages = self._get_detailed_messages()
            collected_messages.update(current_messages)
            logger.info(f"📊 初始加载 {len(current_messages)} 条消息")

            while scroll_attempts < max_scroll_attempts:
                # 向上滚动
                self.driver.execute_script(f"arguments[0].scrollBy(0, {scroll_step});", messages_container)
                scroll_attempts += 1
                time.sleep(1)  # 短暂等待滚动完成
                
                # 等待加载
                time.sleep(SCROLL_WAIT_TIME)
                
                # 获取新消息
                current_messages = self._get_detailed_messages()
                new_count = 0
                for mid, mdata in current_messages.items():
                    if mid not in collected_messages:
                        collected_messages[mid] = mdata
                        new_count += 1
                
                # 记录日志
                logger.info(f"📊 滚动 #{scroll_attempts}: 新增 {new_count} 条消息，总数 {len(collected_messages)}")
                
                # 检查停滞情况
                if new_count == 0:
                    stagnant_rounds += 1
                    logger.info(f"⏳ 无新增消息，停滞次数 {stagnant_rounds}/{max_stagnant_rounds}")
                    
                    # 检查是否达到停滞阈值
                    if stagnant_rounds >= max_stagnant_rounds:
                        logger.info("✅ 已滚动到顶部，没有更多历史消息")
                        break
                else:
                    stagnant_rounds = 0  # 重置停滞计数器
                    
                # 检查容器是否已滚动到顶部
                current_scroll_top = self.driver.execute_script("return arguments[0].scrollTop;", messages_container)
                if current_scroll_top <= 0:
                    logger.info("⬆️ 已滚动到容器顶部，停止滚动")
                    break

            logger.info(f"✅ 最终收集到 {len(collected_messages)} 条唯一消息")

            # 转换为 RawComment 对象
            comments = []
            sorted_messages = sorted(collected_messages.items(), key=lambda x: x[1]['timestamp'] if x[1]['timestamp'] else "")
            
            seen_content = set()  # 用于检测内容重复
            duplicate_count = 0
            
            for i, (msg_id, msg_data) in enumerate(sorted_messages):
                # 检查内容是否重复
                content = msg_data['content'].strip()
                if content in seen_content:
                    duplicate_count += 1
                    continue
                
                seen_content.add(content)
                
                if len(comments) >= max_comments_per_video:
                    break
                    
                try:
                    # 解析时间戳
                    try:
                        if msg_data['timestamp']:
                            if msg_data['timestamp'].endswith('Z'):
                                publish_date = datetime.strptime(msg_data['timestamp'], "%Y-%m-%dT%H:%M:%S.%fZ")
                            else:
                                publish_date = datetime.fromisoformat(msg_data['timestamp'])
                        else:
                            publish_date = datetime.now()
                    except:
                        publish_date = datetime.now()
                    
                    comment = RawComment(
                        top_comment_id=msg_id,
                        video_id=video_info.get("video_id", ""),
                        video_source_id=video_info.get("id", 0),  # 确保设置video_source_id
                        comment_text=content,
                        author=msg_data['author'],
                        like_count=msg_data['like_count'],
                        published_at=publish_date,
                        platform="discord",
                        raw_data=msg_data
                    )
                    comments.append(comment)
                except Exception as e:
                    logger.warning(f"⚠️ 转换消息为评论时出错: {e}")
                    continue

            if duplicate_count > 0:
                logger.info(f"🔄 过滤掉 {duplicate_count} 条重复消息")
                
            logger.info(f"📦 成功转换 {len(comments)} 条消息为评论对象")
            return comments
            
        except Exception as e:
            logger.error(f"❌ 抓取消息失败：{e}")
            return []


    def _get_detailed_messages(self):
        """
        从页面中提取详细的消息信息
        返回一个字典，键为消息ID，值为消息详细信息
        """
        messages = {}
        
        try:
            # 查找所有消息容器 - 改进选择器以更好地匹配Discord消息
            message_containers = self.driver.find_elements(By.CSS_SELECTOR, 
                "[class*=messageListItem] article, " +
                "[class*=message], " +
                "[class*=cozyMessage], " +
                "[id*=message-content], " +
                "[class*=markup]"
            )
            
            logger.debug(f"🔍 找到 {len(message_containers)} 个消息容器")
            
            for container in message_containers:
                try:
                    # 获取消息ID（如果存在）
                    message_id = None
                    try:
                        # 尝试从容器的属性中获取ID
                        message_id = container.get_attribute("id") or container.get_attribute("data-id")
                    except:
                        pass
                    
                    # 获取作者昵称
                    author = "Unknown"
                    try:
                        author_element = container.find_element(By.CSS_SELECTOR, 
                            "[class*=username], " +
                            "[class*=headerText], " +
                            "[data-selenium='username'], " +
                            "[class*=name]")
                        author = author_element.text.strip()
                    except:
                        pass
                    
                    # 获取消息内容
                    content = ""
                    try:
                        content_element = container.find_element(By.CSS_SELECTOR, 
                            "[class*=messageContent], " +
                            "[class*=markup], " +
                            "[class*=content], " +
                            "span:not([class*=emoji])")
                        content = content_element.text.strip()
                    except:
                        pass
                    
                    # 跳过空消息
                    if not content:
                        continue
                        
                    # 获取消息时间戳
                    timestamp = None
                    try:
                        timestamp_element = container.find_element(By.CSS_SELECTOR, 
                            "time, " +
                            "[class*=timestamp], " +
                            "[data-timestamp]")
                        timestamp = timestamp_element.get_attribute("datetime") or timestamp_element.text
                    except:
                        # 如果没有时间戳元素，使用当前时间作为替代
                        timestamp = datetime.now().isoformat()
                    
                    # 获取点赞数
                    like_count = 0
                    try:
                        # 查找包含反应的消息元素
                        reactions = container.find_elements(By.CSS_SELECTOR, 
                            "[class*=reaction], " +
                            "[class*=reactionCount], " +
                            "[class*=reactionInner], " +
                            "[class*=reactionButton]")
                        for reaction in reactions:
                            try:
                                count_text = reaction.text.strip()
                                if count_text.isdigit():
                                    like_count = max(like_count, int(count_text))
                            except:
                                pass
                    except:
                        pass
                    
                    # 获取回复数
                    reply_count = 0
                    try:
                        # 查找回复相关的元素
                        reply_elements = container.find_elements(By.CSS_SELECTOR, 
                            "[class*=replies], " +
                            "[class*=threadMessageCount], " +
                            "[class*=messageReplyCount], " +
                            "[class*=repliesContainer]")
                        for reply_element in reply_elements:
                            try:
                                reply_text = reply_element.text.strip()
                                # 匹配类似 "2 replies" 或 "5 条回复" 的文本
                                import re
                                match = re.search(r'(\d+)', reply_text)
                                if match:
                                    reply_count = int(match.group(1))
                                    break
                            except:
                                pass
                    except:
                        pass
                    
                    # 使用更可靠的唯一标识符
                    if message_id:
                        unique_id = message_id
                    else:
                        # 使用消息内容和时间戳组合作为唯一标识符
                        unique_id = f"{content[:50]}_{timestamp}"  # 取前50个字符避免ID过长
                    
                    messages[unique_id] = {
                        "author": author,
                        "content": content,
                        "timestamp": timestamp,
                        "like_count": like_count,
                        "reply_count": reply_count
                    }
                    
                except Exception as e:
                    # 跳过无法处理的消息
                    continue
                    
        except Exception as e:
            logger.warning(f"⚠️ 提取消息时发生错误: {e}")
            
        return messages

    def get_replies(self, session: Session, video_info):
        """
        Discord 消息系统中暂时不区分主评论和回复
        :param session: 数据库会话
        :param video_info: 视频信息
        """
        logger.info("ℹ️ Discord平台暂不支持单独获取回复评论")
        return []

    def close(self):
        """关闭浏览器驱动"""
        if self.driver:
            self.driver.quit()
            logger.info("🛑 浏览器已关闭")

    def __del__(self):
        """析构函数确保浏览器被关闭"""
        self.close()